/*
** nysiis(): Return the NYSIIS phonetic code for a given input string.
** nysiis_o(): Generate the NYSIIS using the original Taft algorithm.
** 
** The algorithm devised in 1970 as part of the New York State Identification 
** and Intelligence System and is now part of the New York State Criminal 
** Justice Services. It features an accuracy increaes of 2.7% over the 
** traditional Russell Soundex.
**
** NOTE:
** 1. A true NYSIIS is only 6 characters, however some applications want
**    the full length. This routine will return the full length key and leave
**    it to the calling application to truncate the string as they require.
**
**    The length of the output varchar will be the same as the input string.
**
** 2. The nysiis() command will use the alternate algorithm described by
**    Ross Patterson, Rutgers University, May 5, 1988.
**
** 3. The nysiis_o() command will use the original algorithm described by
**    Robert L. Taft, "Name Search Techniques", New York State Identification 
**    and Intelligence System.
**
** References.
**    http://www.dropby.com/NYSIIS.html
*/
void
nysiis_print (
    const char *prefix,
    const char *buffer,
    const short start,
    const short end)
{
    /* This is simply a usefull diagnostic routine which can be called from
    ** nysiis()
    */
    char msg[2048];
    short i, j;

    strcpy(msg, prefix);
    strcat(msg, ": ");

    if (buffer != NULL)
    {
        for (j = strlen(msg), i=start; i <= end && j <= 2046; i++, j++)
        {
            msg[j] = buffer[i];
        };

        msg[j++] = '\000';
    };
    Ingres_trace_function(II_TRACE_FE_MASK, strlen(msg), msg);
}; /* nysiis_print */

II_STATUS
nysiis_o (
    II_SCB        *scb,
    II_DATA_VALUE *string,
    II_DATA_VALUE *rdv)
{
    short i, iv, spaced_out, last_word,
          in_start, in_length,
          prior_is_vowel, next_is_vowel;
    char c, first_char, msg[256], pc, nc;

    struct _buffer {
        char *buffer;
        short buf_start;
        short buf_end;
        short buf_next;
        } buf1, buf2, *from, *to, *swap;
        
    in_length = *(short *)string->db_data;

    /* Reset in_length to point to the first non space character at the end of 
    ** the string.
    */
    do
    {
        c = *(char *)(string->db_data + sizeof(short) + in_length - 1);
        if (! isspace(c)) break;
        in_length--;
    } while (in_length > 0);

    /* Set in_start to the first alpha character in the string */
    for (in_start = 0; in_start <= in_length; in_start++)
    {
        c = *(char *)(string->db_data + sizeof(short) + in_start);
        if (isalpha(c)) break;
    };

    if (in_length - in_start <= 0)
    {
        /* The empty string case */
        *(short *)rdv->db_data = 0;
        return (II_OK);
    };

    /* Get a data buffer */
    buf1.buffer = (char *)malloc((int )in_length * (int )sizeof(char));
    if (buf1.buffer == NULL)
    {
        sprintf(msg,
            "nysiis_o(): Failed to allocate memory for first data buffer!"
        );
        us_error(scb, 0x200012, msg);
        return (II_ERROR);
    };
    buf1.buf_start = 0;
    buf1.buf_end = in_length - 1;
    buf1.buf_next = 0;

    /* Get a data buffer  */
    buf2.buffer = (char *)malloc(in_length * sizeof(char));
    if (buf2.buffer == NULL)
    {
        sprintf(msg,
            "nysiis_o(): Failed to allocate memory for second data buffer!"
        );
        us_error(scb, 0x200013, msg);
        return (II_ERROR);
    };
    buf2.buf_start = 0;
    buf2.buf_end = in_length - 1;
    buf2.buf_next = 0;

    /* Commence preprocessing of input string */

    /* Transfer characters from the input string to the data buffer
    ** Only alphas are transferred to the buffer all other
    ** characters are ignored. ie. no numerics or punctuation.
    ** Lower case alphas are converted to upper case.
    */
    spaced_out = 0; /* Set this when we have internal whitespace. It will
                    ** be used to determine the last_word.
                    */
    last_word = 0;  /* The offset of the last word in the buffer. This will be 
                    ** used in processing suffixes.
                    */

    from = &buf1;
    to = &buf2;

    for (i = in_start, to->buf_next = 0; i < in_length; i++)
    {
        c = *(char *)(string->db_data + sizeof(short) + i);
        if (isalpha(c))
        {
            if (spaced_out)
            {
                spaced_out = 0;
                last_word = to->buf_next;
            };
            *(char *)(to->buffer + to->buf_next++) = (char )toupper(c);
        }
        else if (isspace(c))
        {
            /* flag we have encountered a space! */
            spaced_out = 1;
        };
    };

    to->buf_end = to->buf_next - 1;

    /* Process any suffixes */
    if (last_word)
    {
        /* Drop suffixes made entirely from the roman numerals I or V.
        ** We assume it is (iv = 1) and then examine the trailing characters
        ** until the suffix is completey checked or we find a non IV char
        ** (iv = 0)
        */
        for (iv = 1, i = last_word; i <= to->buf_end; i++)
        {
            c = *(char *)(to->buffer + i);
            if (c != 'I' && c != 'V')
            {
                iv = 0;
                break;
            };
        };

        if (iv)
        {
            to->buf_end = last_word - 1;
        }
        else if (to->buf_end - last_word == 1) /* A two character suffix */
        {
            /* Drop JR/SR suffixes */
             if (!strncmp("JR", (char *)(to->buffer + last_word), 2)
              || !strncmp("SR", (char *)(to->buffer + last_word), 2))
            {
                to->buf_end = to->buf_end - 2;
            };
        };
    };

    /* Commence original algorithm from here */

    /* Step 1: Translate first characters of name */
    if (to->buf_end >= 2) /* At least 3 characters in buffer */
    {
        /* MAC --> MCC */
        if (!strncmp("MAC", (char *)(to->buffer), 3))
        {
            *(char *)(to->buffer + 1) = 'C'; 
        }
        else if (!strncmp("SCH", (char *)(to->buffer), 3))
        {
            *(char *)(to->buffer + 1) = 'S'; 
            *(char *)(to->buffer + 2) = 'S'; 
        }
    };

    if (to->buf_end >= 1) /* At least 2 characters in buffer */
    {
        /* PH --> FF */
        if (!strncmp("PF", (char *)(to->buffer), 2))
        {
            *(char *)(to->buffer)     = 'F';
            *(char *)(to->buffer + 1) = 'F';
        }
        /* PF --> FF */
        else if (!strncmp("PF", (char *)(to->buffer), 2))
        {
            *(char *)(to->buffer)     = 'F';
        }
        /* KN --> NN */
        else if (!strncmp("KN", (char *)(to->buffer), 2))
        {
            *(char *)(to->buffer)     = 'N';
        };
    };

    /* K --> C */
    if (*(char *)(to->buffer) == 'K')
    {
        *(char *)(to->buffer) = 'C';
    };

    /* Step 2: Translate the last characters of the string */
    if (to->buf_end >= 1) /* At least 2 characters in buffer */
    {
        /* EE, IE --> Y */
        if (!strncmp("EE", (char *)(to->buffer + to->buf_end - 1), 2)
         || !strncmp("IE", (char *)(to->buffer + to->buf_end - 1), 2))
        {
            *(char *)(to->buffer + to->buf_end - 1) = 'Y';
            to->buf_end--;
        }
        /* DT, RT, RD, NT, ND --> D.  */
        else if (!strncmp("DT", (char *)(to->buffer + to->buf_end - 1), 2)
              || !strncmp("RT", (char *)(to->buffer + to->buf_end - 1), 2)
              || !strncmp("RD", (char *)(to->buffer + to->buf_end - 1), 2)
              || !strncmp("NT", (char *)(to->buffer + to->buf_end - 1), 2)
              || !strncmp("ND", (char *)(to->buffer + to->buf_end - 1), 2))
        {
            *(char *)(to->buffer + to->buf_end - 1) = 'D';
            to->buf_end--;
        };
    };

#ifdef xDEBUG
    nysiis_print("\n2: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    /* Step 3: First character of key = first character of buffer. */
    first_char = (char )toupper(*(char *)(to->buffer));

    swap = to;
    to = from;
    from = swap;
    /* Step 4: various transcode rules... */
    for (to->buf_next = 0, i = from->buf_start + 1; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */

        if (i + 1 <= from->buf_end) /* At least two characters left */
        {
            if (!strncmp("EV", (char *)(from->buffer + i), 2))
            {
                *(char *)(to->buffer + to->buf_next++) = 'A';
                *(char *)(to->buffer + to->buf_next++) = 'F';
                i++; /* need to skip a character */
                continue;
            };

            if (!strncmp("KN", (char *)(from->buffer + i), 2))
            {
                *(char *)(to->buffer + to->buf_next++) = 'N';
                i++; /* need to skip a character */
                continue;
            };

            if (!strncmp("PH", (char *)(from->buffer + i), 2))
            {
                *(char *)(to->buffer + to->buf_next++) = 'F';
                *(char *)(to->buffer + to->buf_next++) = 'F';
                i++; /* need to skip a character */
                continue;
            };
        };

        if (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U')
        {
            *(char *)(to->buffer + to->buf_next++) = 'A';
            continue;
        };

        if (c == 'Q')
        {
            *(char *)(to->buffer + to->buf_next++) = 'G';
            continue;
        };

        if (c == 'Z')
        {
            *(char *)(to->buffer + to->buf_next++) = 'S';
            continue;
        };

        if (c == 'M')
        {
            *(char *)(to->buffer + to->buf_next++) = 'N';
            continue;
        };

        if (c == 'K')
        {
            *(char *)(to->buffer + to->buf_next++) = 'C';
            continue;
        };

        if (i + 2 <= from->buf_end) /* At least three characters left */
        {
            if (!strncmp("SCH", (char *)(from->buffer + i), 3))
            {
                *(char *)(to->buffer + to->buf_next++) = 'S';
                *(char *)(to->buffer + to->buf_next++) = 'S';
                *(char *)(to->buffer + to->buf_next++) = 'S';
                i+=2; /* need to skip two characters */
                continue;
            };
            
        };

        if (c == 'H' && i > from->buf_start + 1)
        {
            /* NOTE: The rules are poorly defined on 'H'.
            ** Rule is: If previous or next is not a vowel use previous.
            ** But:
            ** 1. Using the previous would be pointless as it could lead to a
            **    duplicated character which would ultimately be removed.
            ** 2. What if the prior character had a transformation? Which
            **    character would we use then.
            ** 3. What if there is no next character?
            ** 
            ** Hence:
            **     We ignore the 'H' if either:
            **     a. this is the last character and the prior was not a vowel,
            **     b. this is not the last character and the prior character or
            **        the next character is not a vowel.
            */
            prior_is_vowel = 0;
            next_is_vowel = 0;

            pc = *(char *)(from->buffer + i - 1);
            if (pc == 'A' || pc == 'E' || pc == 'I' || pc == 'O' || pc == 'U')
                prior_is_vowel = 1;

            if (i < from->buf_end)
            {
                nc = *(char *)(from->buffer + i + 1);
                if (nc == 'A' || nc == 'E' || nc == 'I' || nc == 'O'
                 || nc == 'U') next_is_vowel = 1;
                if (prior_is_vowel == 0 || next_is_vowel == 0) continue;
            }
            else if (prior_is_vowel == 0) continue;
        };

        if (c == 'W' && i > from->buf_start + 1)
        {
            pc = *(char *)(from->buffer + i - 1);
            if (pc == 'A' || pc == 'E' || pc == 'I' || pc == 'O' || pc == 'U')
            {
                /* NOTE: The rules are poorly defined on 'W'.
                ** Rule is: If previous is a vowel use previous.
                ** But:
                ** 1. Using the previous would be pointless as it has already 
                **    been translated to an 'A'. If we use the prior character
                **    as is then a 'non-A' vowel could  sneak into the string.
                ** 2. We could end up with a double 'A', which ultimatley
                **    would be collapsed.
                **
                ** Hence:
                **     We ignore 'W' if it follows a vowel.
                */
                continue;
            };
        };

        /* Add current to key */
        *(char *)(to->buffer + to->buf_next++) = c; 
    };
    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n4: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    /* Step 5: If last character is 'S' remove it. */
    if (*(char *)(to->buffer + to->buf_end) == 'S')
    {
        to->buf_next--;
        to->buf_end--;
    };
#ifdef xDEBUG
    nysiis_print("\n5: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif 

    /* Step 6: If last characters are AY, replace with Y */
    if (to->buf_next - to->buf_start >= 2) /* At least two char in key */
    {
        if (!strncmp("AY", (char *)(to->buffer + to->buf_next - 2), 2))   
        {
            *(char *)(to->buffer + to->buf_next - 2) = 'Y';
            to->buf_next--;
            to->buf_end--;
        };
    };

#ifdef xDEBUG
    nysiis_print("\n6: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif

    /* Step 7: If last character is A, remove it */
    if (*(char *)(to->buffer + to->buf_end) == 'A')
    {
        to->buf_next--;
        to->buf_end--;
    };

#ifdef xDEBUG
    nysiis_print("\n7: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    swap = to;
    to = from;
    from = swap;
    /* Step 8: Collapse any repeated characters. */
    for (to->buf_next = 0, i = from->buf_start; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */

        if (i > from->buf_start)
        {
            pc = *(char *)(from->buffer + i - 1);
            if (pc != c) *(char *)(to->buffer + to->buf_next++) = c;
        }
        else
        {
            *(char *)(to->buffer + to->buf_next++) = c;
        };
    };
    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n8: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    swap = to;
    to = from;
    from = swap;
    /* Step 9: Add first char of name as first char of key */
    *(char *)(rdv->db_data + sizeof(short)) = first_char;

    /* Now write the rest of the key to the rdv */
    memcpy(
        (char *)(rdv->db_data + sizeof(short) + 1),
        (char *)(from->buffer + from->buf_start),
        from->buf_next - from->buf_start
    );
    *(short *)rdv->db_data = from->buf_next - from->buf_start + 1;

    free (buf1.buffer);
    free (buf2.buffer);

    return (II_OK);
}; /* nysiis_o */

II_STATUS
nysiis_a (
    II_SCB        *scb,
    II_DATA_VALUE *string,
    II_DATA_VALUE *rdv)
{
    short i, iv, got_dts, spaced_out, last_word,
          in_start, in_length, out_length,
          buf_next,
          prior_is_vowel, next_is_vowel;
    char c, first_char, msg[256], pc, nc;

    struct _buffer {
        char *buffer;
        short buf_start;
        short buf_end;
        short buf_next;
        } buf1, buf2, *from, *to, *swap;
        
    in_length = *(short *)string->db_data;

    /* Reset in_length to point to the first non space character at the end of 
    ** the string.
    */
    do
    {
        c = *(char *)(string->db_data + sizeof(short) + in_length - 1);
        if (! isspace(c)) break;
        in_length--;
    } while (in_length > 0);

    /* Set in_start to the first alpha character in the string */
    for (in_start = 0; in_start <= in_length; in_start++)
    {
        c = *(char *)(string->db_data + sizeof(short) + in_start);
        if (isalpha(c)) break;
    };

    if (in_length - in_start <= 0)
    {
        /* The empty string case */
        *(short *)rdv->db_data = 0;
        return (II_OK);
    };

    /* Get a data buffer */
    buf1.buffer = (char *)malloc((int )in_length * (int )sizeof(char));
    if (buf1.buffer == NULL)
    {
        sprintf(msg,
            "nysiis(): Failed to allocate memory for first data buffer!"
        );
        us_error(scb, 0x200012, msg);
        return (II_ERROR);
    };
    buf1.buf_start = 0;
    buf1.buf_end = in_length - 1;
    buf1.buf_next = 0;

    /* Get a data buffer  */
    buf2.buffer = (char *)malloc(in_length * sizeof(char));
    if (buf2.buffer == NULL)
    {
        sprintf(msg,
            "nysiis(): Failed to allocate memory for second data buffer!"
        );
        us_error(scb, 0x200013, msg);
        return (II_ERROR);
    };
    buf2.buf_start = 0;
    buf2.buf_end = in_length - 1;
    buf2.buf_next = 0;

    /* Commence preprocessing of input string */

    /* Transfer characters from the input string to the data buffer
    ** Only alphas are transferred to the buffer all other
    ** characters are ignored. ie. no numerics or punctuation.
    ** Lower case alphas are converted to upper case.
    */
    spaced_out = 0; /* Set this when we have internal whitespace. It will
                    ** be used to determine the last_word.
                    */
    last_word = 0;  /* The offset of the last word in the buffer. This will be 
                    ** used in processing suffixes.
                    */

    from = &buf1;
    to = &buf2;

    for (i = in_start, to->buf_next = 0; i < in_length; i++)
    {
        c = *(char *)(string->db_data + sizeof(short) + i);
        if (isalpha(c))
        {
            if (spaced_out)
            {
                spaced_out = 0;
                last_word = to->buf_next;
            };
            *(char *)(to->buffer + to->buf_next++) = (char )toupper(c);
        }
        else if (isspace(c))
        {
            /* flag we have encountered a space! */
            if (! spaced_out) spaced_out = 1;
        };
    };

    to->buf_end = to->buf_next - 1;

    /* Wind back any trailing space in the buffer. There should only be one */
    if (*(char *)(to->buffer + to->buf_end) == ' ') to->buf_end--;
        
    /* Process any suffixes */
    if (last_word)
    {
        /* Drop suffixes made entirely from the roman numerals I or V.
        ** We assume it is (iv = 1) and then examine the trailing characters
        ** until the suffix is completey checked or we find a non IV char
        ** (iv = 0)
        */
        for (iv = 1, i = last_word; i <= to->buf_end; i++)
        {
            c = *(char *)(to->buffer + i);
            if (c != 'I' && c != 'V')
            {
                iv = 0;
                break;
            };
        };

        if (iv)
        {
            to->buf_end = last_word - 1;
        }
        else if (to->buf_end - last_word == 1) /* A two character suffix */
        {
            /* Drop JR/SR suffixes */
             if (!strncmp("JR", (char *)(to->buffer + last_word), 2)
              || !strncmp("SR", (char *)(to->buffer + last_word), 2))
            {
                to->buf_end = to->buf_end - 2;
            };
        };
    };

    /* Commence modifed algorithm from here */

    /* Step 2: remove all trailing S or Z ... not nec. a suffix. */
    for (; to->buf_end > 0; to->buf_end--)
    {
        c = *(char *)(to->buffer + to->buf_end);
        if (c != 'S' && c != 'Z') break;
    };

    if (to->buf_end <= 0)
    {
        /* The empty string case */
        *(short *)rdv->db_data = 0;
        return (II_OK);
    };

    /* Step 3: Translate first characters of name.
    **         Note that at this point to->buf_start == 0
    */
    if (to->buf_end >= 1) /* At least two characters in buffer */
    {
        /* PF --> F */
        if (!strncmp("PF", (char *)(to->buffer), 2))
        {
            to->buf_start = 1;
        }
        else if (to->buf_end >= 2) /* At least three characters in buffer */
        {
            /* MAC --> MC */
            if (!strncmp("MAC", (char *)(to->buffer), 3))
            {
                *(char *)(to->buffer + 1) = 'M'; 
                to->buf_start = 1;
            };
        };
    };
#ifdef xDEBUG
    nysiis_print("\n3:Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif

    /* From here onwards you must allow for buf_start being non zero */

    /* Step 4: Translate the last characters of the string */
    if (to->buf_end - to->buf_start >= 1) /* At least 2 characters in buffer */
    {
        /* IX --> IC */
        if (!strncmp("IX", (char *)(to->buffer + to->buf_end - 1), 2))
        {
            *(char *)(to->buffer +  to->buf_end) = 'C';
        }
        /* EX --> EC */
        else if (!strncmp("EX", (char *)(to->buffer + to->buf_end - 1), 2))
        {
            *(char *)(to->buffer +  to->buf_end) = 'C';
        }
        /* YE, EE, IE --> Y */
        else if (!strncmp("YE", (char *)(to->buffer + to->buf_end - 1), 2)
              || !strncmp("EE", (char *)(to->buffer + to->buf_end - 1), 2)
              || !strncmp("IE", (char *)(to->buffer + to->buf_end - 1), 2))
        {
            *(char *)(to->buffer + to->buf_end - 1) = 'Y';
            to->buf_end--;
        }
        /* DT, RT, RD, NT, ND --> D (repeat this step as necessary) */
        else 
        {
            do
            {
                got_dts = 0;
                if (!strncmp("DT", (char *)(to->buffer + to->buf_end - 1), 2)
                 || !strncmp("RT", (char *)(to->buffer + to->buf_end - 1), 2)
                 || !strncmp("RD", (char *)(to->buffer + to->buf_end - 1), 2)
                 || !strncmp("NT", (char *)(to->buffer + to->buf_end - 1), 2)
                 || !strncmp("ND", (char *)(to->buffer + to->buf_end - 1), 2))
                {
                    got_dts = 1;
                    *(char *)(to->buffer + to->buf_end - 1) = 'D';
                    to->buf_end--;
                };
           } while (got_dts && to->buf_end >= 1);
        };
    };

    /* Step 5: Translate EV to EF if not at start of name */
    for (i = to->buf_start + 1; i < to->buf_end; i++)
    {
        if (!strncmp("EV", (char *)(to->buffer + i), 2))
        {
            *(char *)(to->buffer + i + 1) = 'F';
            i++;
        };
    };

#ifdef xDEBUG
    nysiis_print("\n6:Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    /* Step 6: First character of key = first character of buffer. */
    first_char = (char )toupper(*(char *)(to->buffer + to->buf_start));

    /* Note that from here each step is meant to be applied to the entire
    ** string making a new string. The next step is then applied to that new
    ** string and so on...
    ** This means we have to process the string from buf1.buffer to
    ** buf2.buffer and back as required...
    ** Not terribly efficient but it has to be done this way rather than
    ** character at a time as there are too many cases where the latter
    ** approach would end in tears.
    */

    swap = to;
    to = from;
    from = swap;
    /* Step 7: Remove any 'W' that follows a vowel. */
    for (to->buf_next = to->buf_start, i = from->buf_start; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */

        if (i > from->buf_start && c == 'W')
        {
            pc = *(char *)(from->buffer + i - 1);
            if (pc != 'A' && pc != 'E' && pc != 'I' && pc != 'O' && pc != 'U')
            {
                *(char *)(to->buffer + to->buf_next++) = 'W';
            };
            continue;
        };
        /* Anything else just gets put into key as-is */
        *(char *)(to->buffer + to->buf_next++) = c; 
    };
    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n7: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    swap = to;
    to = from;
    from = swap;
    /* Step 8: Replace all vowels with 'A', collapse all 'A's */
    for (to->buf_next = to->buf_start, i = from->buf_start; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */
        if (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U')
        {
            if (to->buf_next)
            {
                if (*(char *)(to->buffer + to->buf_next - 1) != 'A')
                {
                    *(char *)(to->buffer + to->buf_next++) = 'A';
                };
            }
            else
            {
                *(char *)(to->buffer + to->buf_next++) = 'A';
            };
            continue;
        };
        /* Anything else just gets put into key as-is */
        *(char *)(to->buffer + to->buf_next++) = c; 
    };

    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n8: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    swap = to;
    to = from;
    from = swap;
    /* Step 9: GHT --> GT */
    for (to->buf_next = to->buf_start, i = from->buf_start; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */
        if (i + 2 <= from->buf_end) /* At least 3 char left in buffer */
        {
            if (!strncmp("GHT", (char *)(from->buffer + i), 3))
            {
                *(char *)(to->buffer + to->buf_next++) = 'G';
                *(char *)(to->buffer + to->buf_next++) = 'T';
                i+=2;
                continue;
            };
        };
        /* Anything else just gets put into key as-is */
        *(char *)(to->buffer + to->buf_next++) = c; 
    };

    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n9: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    swap = to;
    to = from;
    from = swap;
    /* Step 10: DG --> G */
    for (to->buf_next = to->buf_start, i = from->buf_start; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */
        if (i + 1 <= from->buf_end) /* At least 2 char left in buffer */
        {
            if (!strncmp("DG", (char *)(from->buffer + i), 2))
            {
                *(char *)(to->buffer + to->buf_next++) = 'G';
                i++;
                continue;
            }
        };
        /* Anything else just gets put into key as-is */
        *(char *)(to->buffer + to->buf_next++) = c; 
    };

    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n10: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    swap = to;
    to = from;
    from = swap;
    /* Step 11: PH --> F */
    for (to->buf_next = to->buf_start, i = from->buf_start; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */
        if (i + 1 <= from->buf_end) /* At least 2 char left in buffer */
        {
            if (!strncmp("PH", (char *)(from->buffer + i), 2))
            {
                *(char *)(to->buffer + to->buf_next++) = 'F';
                i++;
                continue;
            }
        };
        /* Anything else just gets put into key as-is */
        *(char *)(to->buffer + to->buf_next++) = c; 
    };

    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n11: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    swap = to;
    to = from;
    from = swap;
    /* Step 12: If not first character eliminate any 'H' preceded or
    **          followed by a vowel.
    */
    for (to->buf_next = to->buf_start, i = from->buf_start; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */
        if (i > from->buf_start && c == 'H')
        {
            pc = *(char *)(from->buffer + i - 1);
            prior_is_vowel = 0;
            if (pc == 'A' || pc == 'E' || pc == 'I' || pc == 'O' || pc == 'U')
                prior_is_vowel = 1;

            next_is_vowel = 0;
            if (i < from->buf_end)
            {
                nc = *(char *)(from->buffer + i + 1);
                if (nc == 'A' || nc == 'E' || nc == 'I' || nc == 'O' || nc == 'U')
                    next_is_vowel = 1;
            };

            if (prior_is_vowel || next_is_vowel) continue;
        };
        /* Anything else just gets put into key as-is */
        *(char *)(to->buffer + to->buf_next++) = c; 
    };
    
    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n12: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    swap = to;
    to = from;
    from = swap;
    /* Step 13: Change KN to N else K to C */
    for (to->buf_next = to->buf_start, i = from->buf_start; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */
        if (i + 1 <= from->buf_end) /* At least 2 char left in buffer */
        {
            if (!strncmp("KN", (char *)(from->buffer + i), 2))
            {
                *(char *)(to->buffer + to->buf_next++) = 'N';
                i++; /* Note we need to do an extra i++ at end of for loop */
                continue;
            };
        };
        
        if (c == 'K')
        {
            *(char *)(to->buffer + to->buf_next++) = 'C';
            continue;
        };
        /* Anything else just gets put into key as-is */
        *(char *)(to->buffer + to->buf_next++) = c; 
    };

    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n13: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    swap = to;
    to = from;
    from = swap;
    /* Step 14: If not first char, change M to N */
    for (to->buf_next = to->buf_start, i = from->buf_start; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */
        if (i > from->buf_start && c == 'M')
        {
            *(char *)(to->buffer + to->buf_next++) = 'N';
            continue;
        };
        /* Anything else just gets put into key as-is */
        *(char *)(to->buffer + to->buf_next++) = c; 
    };

    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n14: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    swap = to;
    to = from;
    from = swap;
    /* Step 15: If not first char, change Q to G */
    for (to->buf_next = to->buf_start, i = from->buf_start; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */
        if (i > from->buf_start && c == 'Q')
        {
            *(char *)(to->buffer + to->buf_next++) = 'G';
            continue;
        };
        /* Anything else just gets put into key as-is */
        *(char *)(to->buffer + to->buf_next++) = c; 
    };

    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n15: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    swap = to;
    to = from;
    from = swap;
    /* Step 16: 'SH' --> 'S' */
    for (to->buf_next = to->buf_start, i = from->buf_start; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */
        if (i + 1 <= from->buf_end) /* At least 2 char left in buffer */
        {
            if (!strncmp("SH", (char *)(from->buffer + i), 2))
            {
                *(char *)(to->buffer + to->buf_next++) = 'S';
                i++; /* Note we need to do an extra i++ at end of for loop */
                continue;
            };
        };
        /* Anything else just gets put into key as-is */
        *(char *)(to->buffer + to->buf_next++) = c; 
    };

    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n16: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    swap = to;
    to = from;
    from = swap;
    /* Step 17: 'SCH' --> 'S' */
    for (to->buf_next = to->buf_start, i = from->buf_start; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */
        if (i + 2 <= from->buf_end) /* At least 3 char left in buffer */
        {
            if (!strncmp("SCH", (char *)(from->buffer + i), 3))
            {
                *(char *)(to->buffer + to->buf_next++) = 'S';
                i+=2; /* Note we need to do an extra i++ at end of for loop */
                continue;
            };
        };
        /* Anything else just gets put into key as-is */
        *(char *)(to->buffer + to->buf_next++) = c; 
    };

    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n17: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    swap = to;
    to = from;
    from = swap;
    /* Step 18: 'YW' --> Y */
    for (to->buf_next = to->buf_start, i = from->buf_start; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */
        if (i + 1 <= from->buf_end) /* At least 2 char left in buffer */
        {
            if (!strncmp("YW", (char *)(from->buffer + i), 2))
            {
                *(char *)(to->buffer + to->buf_next++) = 'Y';
                i++; /* Note we need to do an extra i++ at end of for loop */
                continue;
            };
        };
        /* Anything else just gets put into key as-is */
        *(char *)(to->buffer + to->buf_next++) = c; 
    };

    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n18: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    swap = to;
    to = from;
    from = swap;
    /* Step 19: 'Y' --> 'A' unless first or last character */
    for (to->buf_next = to->buf_start, i = from->buf_start; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */
        if (i > from->buf_start && i < from->buf_end && c == 'Y')
        {
            *(char *)(to->buffer + to->buf_next++) = 'A';
            continue;
        };
        /* Anything else just gets put into key as-is */
        *(char *)(to->buffer + to->buf_next++) = c; 
    };

    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n19: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif

    swap = to;
    to = from;
    from = swap;
    /* Step 20: 'WR' --> 'R' */
    for (to->buf_next = to->buf_start, i = from->buf_start; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */
        if (i + 1 <= from->buf_end) /* At least 2 char left in buffer */
        {
            if (!strncmp("WR", (char *)(from->buffer + i), 2))
            {
                *(char *)(to->buffer + to->buf_next++) = 'R';
                i++; /* Note we need to do an extra i++ at end of for loop */
                continue;
            };
        };
        /* Anything else just gets put into key as-is */
        *(char *)(to->buffer + to->buf_next++) = c; 
    };

    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n20: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    swap = to;
    to = from;
    from = swap;
    /* Step 21: If not first character change 'Z' to 'S' */
    for (to->buf_next = to->buf_start, i = from->buf_start; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */
        if (i > from->buf_start && c == 'Z')
        {
            *(char *)(to->buffer + to->buf_next++) = 'S';
            continue;
        };

        /* Anything else just gets put into key as-is */
        *(char *)(to->buffer + to->buf_next++) = c; 
    }; /* foreach character in the buffer */

    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n21: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif

    /* Step 22: If last characters are AY, replace with Y */
    if (to->buf_next - to->buf_start >= 2) /* At least two char in key */
    {
        if (!strncmp("AY", (char *)(to->buffer + to->buf_next - 2), 2))   
        {
            *(char *)(to->buffer + to->buf_next - 2) = 'Y';
            to->buf_next--;
            to->buf_end--;
        };
    };

#ifdef xDEBUG
    nysiis_print("\n22: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif

    /* Step 23: Remove trailing vowels */
    for (i = to->buf_end; i >= to->buf_start; i--)
    {
        c = *(char *)(to->buffer + i);
        if (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U')
        {
            to->buf_next--;
            to->buf_end--;
        }
        else
        {
            break;
        };
    };

#ifdef xDEBUG
    nysiis_print("\n23: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    swap = to;
    to = from;
    from = swap;
    /* Step 24: Collapse any repeated characters. */
    for (to->buf_next = to->buf_start, i = from->buf_start; i <= from->buf_end; i++)
    {
        c = *(char *)(from->buffer + i); /* The current character */

        if (i > from->buf_start)
        {
            pc = *(char *)(from->buffer + i - 1);
            if (pc != c) *(char *)(to->buffer + to->buf_next++) = c;
        }
        else
        {
            *(char *)(to->buffer + to->buf_next++) = c;
        };
    };

    to->buf_end = to->buf_next - 1;
#ifdef xDEBUG
    nysiis_print("\n24: Buffer is", to->buffer, to->buf_start, to->buf_end);
#endif
    swap = to;
    to = from;
    from = swap;
    /* Step 25: if first char of original string is a vowel, replace
    **          the first transcoded 'A'.
    */
    c = first_char;
    if (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U')
    {
        for (i = from->buf_start; i <= from->buf_end; i++)
        {
            if (*(char *)(from->buffer + i) == 'A')
            {
                *(char *)(from->buffer + i) = first_char;
                break;
            };
        };
    };

#ifdef xDEBUG
    nysiis_print("\n25: Buffer is", from->buffer, from->buf_start, from->buf_end);
#endif
    /* Now write it all to the rdv */
    memcpy(
        (char *)(rdv->db_data + sizeof(short)),
        (char *)(from->buffer + from->buf_start),
        from->buf_next - from->buf_start
    );
    *(short *)rdv->db_data = from->buf_next - from->buf_start;

    free (buf1.buffer);
    free (buf2.buffer);

    return (II_OK);
}; /* nysiis_a */
